CCDL 2020

Objective for this notebook analysis:

We’ll use the same gene expression dataset we used in the previous notebook. It a pre-processed astrocytoma microarray dataset that we performed differential expression analyses.

More tidyverse resources:
- R for Data Science - Tidyverse documentation - Cheatsheet of Tidyverse data transformation - Online Tidyverse book chapter

Concepts to cover (in order) - Referencing a library’s function with :: - dplyr::select - dplyr::filter - dplyr::mutate - apply functions? - Writing to RDS/TSV

Set Up

Declare the name of the directory where we will read in the data.

data_dir <- "data"

The tidyverse is a collection of packages that are handy for general data wrangling, analysis, and visualization. Other packages that are specifically handy for different biological analyses are found on Bioconductor. If we want to use a package’s functions we first need to install them. In our Docker container, we already have tidyverse and other packages we will use in this workshop installed for you. But if you needed to install it or other packages available on CRAN, you do it using the install.packages function like this: install.packages("tidyverse")

library(tidyverse)

Referencing a library’s function with ::

Note that if we had not imported the tidyverse set of packages using library like above, and we were using a tidyverse function like read_tsv, we would need to tell R what package to find this function in. To do this, we would use :: to tell R to load in this function from the readr package by using readr::read_tsv. You will see this :: method of referencing libraries throughout the course.

Managing directories

Before we can import the data we need, we should double check where R is looking for files aka our what our working directory currently is. We can do this by using the getwd() function, which will tell us what folder we are in.

# Let's check what directory we are in:
getwd()
[1] "/home/rstudio/kitematic/training-modules/intro-to-R-tidyverse"

Note that for Rmd files, the working directory is always wherever the file is located.

We will want to make a directory for our output and we will call this directory: results. But before we create the directory, we should check if it already exists. We will show two ways that we can do this.

First, we can use the dir() function to have R list the files in our working directory.

# Let's check what files are here
dir()
 [1] "00a-rstudio_guide.md"           "00b-debugging_resources.md"    
 [3] "01-introduction-base-R.nb.html" "01-introduction-base-R.Rmd"    
 [5] "02-intro_to_ggplot2.nb.html"    "02-intro_to_ggplot2.Rmd"       
 [7] "03-intro_to_tidyverse.nb.html"  "03-intro_to_tidyverse.Rmd"     
 [9] "data"                           "diagrams"                      
[11] "metadata.tsv"                   "plots"                         
[13] "README.md"                      "refine.bio"                    
[15] "results"                        "screenshots"                   
[17] "scripts"                       

This shows us there is no folder called “results” yet.

If we want to more pointedly look for “results” in our working directory we can use the dir.exists function.

# Check if the results directory exists
dir.exists("results")
[1] TRUE

If the above says FALSE that means we will need to create a results directory using the function dir.create.

# Make a directory within the working directory called 'results'
dir.create("results")
'results' already exists

After creating the results directory above, let’s re-run dir.exists to see if now it exists.

# Re-check if the results directory exists
dir.exists("results")
[1] TRUE

We can use the output of dir.exists to automatically create or hold off on creating a directory by putting this together in an if statement like below. Note that we put an exclamation point to signify that we want a directory to be created only if dir.exists(results) is NOT equal to TRUE.

# If 'results' directory doesn't exist...
if (!dir.exists("results")) {
  # ... create a 'results' directory
  dir.create("results")
}

Note that the dir.exists function will not work on files themselves. In this case, there is an analogous function called file.exists.

Try using the file.exists function to see if the file gene_results_GSE44971.tsv file exists in the current directory. Use the code chunk we set up for you below. Note that in our notebooks (and sometimes elsewhere), wherever you see a <FILL_IN_THE_BLANK> like in the chunk below, that is meant for you to replace with the correct phrase before you run the chunk (otherwise you will get an error).

# Replace the <PUT_FILE_NAME_HERE> with the name of the file you are looking for
# Remember to use quotes to make it a character string
file.exists(<PUT_FILE_NAME_HERE>)

Now that we’ve determined that gene_results_GSE44971.tsv exists, we are ready to read it into our R environment.

Read a TSV file

In R, we will use file.path() for this (more on this later in this notebook).
Although base R has read-in functions, the readr functions are faster and more straightforward to use so we are going to use those here. Because the file we are reading in is a TSV (tab separated values) file we will be using the read_tsv function. But note that there are analogous functions for CSV (comma separated values) files (read_csv) and other files types.

Read in the differential expression analysis results file

Here we are using a tidyverse function read_tsv from a library of functions called readr.

stats_df <- readr::read_tsv(file.path(data_dir,
                                      "gene_results_GSE44971.tsv"))
Parsed with column specification:
cols(
  ensembl_id = col_character(),
  gene_symbol = col_character(),
  contrast = col_character(),
  log_fold_change = col_double(),
  avg_expression = col_double(),
  t_statistic = col_double(),
  p_value = col_double(),
  adj_p_value = col_double()
)
# The file we are reading in is our argument for read_tsv
gene_df <- read_tsv(file.path(data_dir, "GSE44971.tsv"))
Parsed with column specification:
cols(
  .default = col_double(),
  Gene = col_character()
)
See spec(...) for full column specifications.

Use this chunk to explore what gene_df looks like.

# Explore `gene_df`

What information is contained in gene_df?

dplyr pipes

A nifty thing with the tidyverse is the pipes: %>% This handy thing allows you to funnel the result of one expression to the next making your code a little more streamlined.

For example, the output from this:

filter(stats_df, contrast == "male_female")

…is the same as the output from this:

stats_df %>% filter(contrast == "male_female")

This makes it so your code is cleaner and easier to read. Let’s look at an example with our stats of of how the same functions look with or without pipes:

Example 1: without pipes:

stats_nopipe <- stats_df
stats_nopipe <- arrange(stats_nopipe, t_statistic)
stats_nopipe <- filter(stats_nopipe, avg_expression > 5)
stats_nopipe <- select(stats_nopipe, contrast, log_fold_change, p_value)

UGH, we have to repeat and assign stats_nopipe so many times here! It’s annoying and makes it harder for people to read.

Example 2: Same result as 1 but with pipes!

# Example of the same modifications as above but with pipes!
stats_pipe  <- stats_df %>%
               arrange(t_statistic) %>%
               filter(avg_expression > 5) %>%
               select(contrast, log_fold_change, p_value)

Let’s double check that these are the same by using the function, all.equal:

all.equal(stats_nopipe, stats_pipe)
[1] TRUE

all.equal is letting us know that these two objects are the same. (You can use all.equal in other instances and adjust the parameters depending on how exact of a match you are looking for. )

Now that hopefully you are convinced that tidyverse helps your code to be neater and easier to use and read, let’s go through some of the popular tidyverse functions and so we can create pipelines like this.

Common tidyverse functions

Let’s say we wanted to filter this gene expression dataset to particular sample groups. In order to do this, we would use the function filter as well as a logic statement.

# Here let's filter the data to a particular gene
stats_df %>% filter(gene_symbol == "SNCA")

We can use filter similarly for numeric statements.

# Here let's filter the data to have average expression values above 5
stats_df %>% filter(avg_expression > 5)

We can apply multiple filters at once:

stats_df %>% filter(contrast == "male_female", 
                    avg_expression > 5)

When we are filtering, the %in% operator can come in handy if we have multiple items we would like to match. Let’s take a look at what using %in% does.

stats_df$gene_symbol %in% c("SNCA", "CDKN1A")
   [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [14] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [27] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [40] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [53] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [66] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [79] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  [92] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [105] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [118] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [131] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [144] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [157] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [170] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [183] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [196] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [209] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [222] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [235] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [248] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [261] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [274] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [287] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [300] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [313] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [326] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [339] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [352] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [365] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [378] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [391] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [404] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [417] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [430] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [443] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [456] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [469] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [482] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [495] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [508] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [521] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [534] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [547] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [560] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [573] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [586] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [599] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [612] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [625] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [638] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [651] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [664] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [677] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [690] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [703] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [716] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [729] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [742] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [755] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [768] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [781] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [794] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [807] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [820] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [833] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [846] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [859] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [872] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [885] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [898] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [911] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [924] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [937] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [950] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [963] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [976] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [989] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [ reached getOption("max.print") -- omitted 1512 entries ]

%in% returns a logical vector that now we can use in dplyr::filter.

stats_df %>% filter(gene_symbol %in% c("SNCA", "CDKN1A"))

Let’s return to our first filter and build on to it. This time, let’s keep only some of these variables with the select function. Let’s also save this as a new data.frame called stats_filtered_df.

stats_filtered_df <- stats_df %>% 
  filter(contrast == "male_female", 
         avg_expression > 5) %>%
  select(log_fold_change, t_statistic)

Let’s say we wanted to arrange this dataset so that the patients were arranged youngest to oldest. In order to do this, we would use the function arrange as well as the variable we would like to sort by (in this case age_mths).

stats_df %>% arrange(p_value) 

What if we want the the oldest patients at the top of this dataset instead? We can use the same function, but instead use the desc function too.

stats_df %>% arrange(desc(p_value))

What if we would like to create new variables? For that we use mutate function.

stats_df %>% 
  mutate(log10 = -log10(p_value))

What if we want to obtain summary statistics for a variable? The summarize function allows us to calculate summary statistics for a column. Here we will use summarize to obtain an average expression overall all genes.

stats_df %>% summarize(mean(log_fold_change))

What if we’d like to obtain a summary statistics but have them for various groups? Conveniently named, there’s a function called group_by that seamlessly allows us to do this. Also note that group_by allows us to group by multiple variables at a time if you want to.

stats_summary_df <- stats_df %>%
      group_by(contrast) %>% 
      summarize(mean(log_fold_change))

Let’s look at a preview of what we made:

stats_summary_df

Chunking out your code: a technique for figuring out what’s happening

We did quite a few steps to get the object stats_summary_df. If we needed to troubleshoot this step, or if we got confused on what’s happening, it could be difficult to figure this out by looking at the code chunk all at once.

For these situations, we are going to introduce you to a technique called “chunking”.

Printing out smaller units of code at a time can help you learn what is happening at each step. This will help fast-track your troubleshooting attempts.

Use this empty code chunk below to try chunking out this code chunk by following the figure above.

# Try to chunk out lines of code we used to get `stats_summary_df`

For more troubleshooting techniques or a breakdown of the most common R errors, check out the debugging resources guide in this repository.

A brief intro to the apply family of functions

In base R there the apply family of functions can be an alternative methods for performing transformations across a data.frame, matrix or for other object structures.

The second argument in apply specifies whether we are applying something across rows or across columns (1 for rows, 2 for columns).

# Calculate row means
gene_means <- apply(gene_df[, -1], 1, mean) # Notice we are using 1 here

# How long will `gene_means` be? 
length(gene_means)
[1] 20056

Why do we have a [, -1] after gene_df in the above chunk? Now let’s investigate the same set up, but use 2 to apply over the columns of our matrix.

# Calculate sample means
sample_means <- apply(gene_df[, -1], 2, mean) # Notice we use 2 here

# How long will `sample_means` be? 
length(sample_means)
[1] 58

Although the apply functions may not be as easy to use as the tidyverse functions, for some applications, apply methods can be better suited. In this workshop, we will not delve too deeply into the various other apply functions (tapply, lapply, etc.) but you can read more examples about them here. ## The dplyr::join functions

Let’s say we have a scenario where we have two data.frames that we would like to combine. Recall that stats_pipe and gene_df are data.frames that contain information about some of the same genes. The dplyr::join family of functions are useful for various scenarios of combining data.frames.

For now, we will focus on inner_join, which will combine data.frames by only keeping information about the samples that are in both data.frames. We need to use the by argument to designate what column(s) should be used as a key to match the data.frames (in this case ensembl_ids).

stats_df %>% inner_join(gene_df, 
                        by = c('ensembl_id' = 'Gene')) 

Note that if there are columns of the same name in both data.frames, the default is to save both columns but a x or y is added to the name to note which data.frame it originated from.

Save data to files

Save to TSV files

Let’s write this data we edited to a file. To do this, we can use the readr library of write functions. Note that the second argument of write_tsv needs to be a character string that contains our file.path to the new file we would like to create. Remember that we created a results directory to put our output in. But if we want to save our data to a directory other than our working directory, we need to specify this. This is what we will use the file.path function for. Let’s look at what file.path does, by taking a look at the print out of it from examples below.

# Which of these file paths is what we want to use to save our data to the
# results directory we created at the beginning of this notebook?
file.path("docker-install", "stats_summary.tsv")
[1] "docker-install/stats_summary.tsv"
file.path("results", "stats_summary.tsv")
[1] "results/stats_summary.tsv"
file.path("stats_summary.tsv", "results")
[1] "stats_summary.tsv/results"

Replace <NEW_FILE_PATH> below with the file.path from above that will successfully save our file to the results folder

# Write our data.frame to a TSV file
write_tsv(stats_summary_df, <NEW_FILE_PATH>)

Check in your results directory to see if your new file has successfully saved.

Save to RDS files

For this example we have been working with data.frames, however, in other situations we may want to save more complicated structure objects or very large objects. RDS (R Data Serialized/Single) files may be a better option in these instances. RDS is R’s special file format for holding data exactly as you have it in your R environment. RDS files can also be compressed, meaning they will take up less space on your computer. Let’s save our data to an RDS file in our results folder. You will need to replace the .tsv with .RDS, but you can use what we determined as our file.path for the last chunk as your template.

# Write your object to an RDS file
write_rds(stats_summary, <PUT_CORRECT_FILE_PATH_HERE>)

Read an RDS file

Since now you have learned the readr functions: read_tsv, write_tsv, and now, write_rds, what do you suppose the function you will need to read your RDS file is called? Use that function here to re-import your data in the chunk we set up for you below.

# Read in your RDS file
reimport_df <- <PUT_FUNCTION_NAME>(file.path("results", "stats_clean.RDS"))

As is good practice, we will end this session by printing out our session info.

Session Info

# Print out the versions and packages we are using in this session
sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.2     readr_1.3.1    
[6] tidyr_0.8.3     tibble_2.1.3    ggplot2_3.2.0   tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1           lubridate_1.7.4      lattice_0.20-38     
 [4] assertthat_0.2.1     zeallot_0.1.0        rprojroot_1.3-2     
 [7] digest_0.6.20        utf8_1.1.4           R6_2.4.0            
[10] cellranger_1.1.0     backports_1.1.4      stats4_3.6.0        
[13] RSQLite_2.1.1        evaluate_0.14        httr_1.4.0          
[16] pillar_1.4.2         rlang_0.4.0          lazyeval_0.2.2      
[19] readxl_1.3.1         rstudioapi_0.10      blob_1.1.1          
[22] S4Vectors_0.24.1     rmarkdown_1.13       labeling_0.3        
[25] bit_1.1-14           munsell_0.5.0        broom_0.5.2         
[28] compiler_3.6.0       modelr_0.1.4         xfun_0.8            
[31] pkgconfig_2.0.2      BiocGenerics_0.32.0  base64enc_0.1-3     
[34] htmltools_0.3.6      tidyselect_0.2.5     IRanges_2.20.1      
[37] fansi_0.4.0          crayon_1.3.4         withr_2.1.2         
[40] grid_3.6.0           nlme_3.1-140         jsonlite_1.6        
[43] gtable_0.3.0         DBI_1.0.0            magrittr_1.5        
[46] scales_1.0.0         cli_1.1.0            stringi_1.4.3       
[49] limma_3.42.0         xml2_1.2.0           vctrs_0.1.0         
[52] generics_0.0.2       org.Hs.eg.db_3.10.0  tools_3.6.0         
[55] bit64_0.9-7          Biobase_2.46.0       glue_1.3.1          
[58] hms_0.4.2            rsconnect_0.8.13     parallel_3.6.0      
[61] yaml_2.2.0           AnnotationDbi_1.48.0 colorspace_1.4-1    
[64] BiocManager_1.30.4   rvest_0.3.4          memoise_1.1.0       
[67] knitr_1.23           haven_2.1.1         
LS0tCnRpdGxlOiAiSW50cm8gdG8gZ2dwbG90MiIKb3V0cHV0OiAgIAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgoqKkNDREwgMjAyMCoqCgojIyBPYmplY3RpdmUgZm9yIHRoaXMgbm90ZWJvb2sgYW5hbHlzaXM6IAoKV2UnbGwgdXNlIHRoZSBzYW1lIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHdlIHVzZWQgaW4gdGhlIHByZXZpb3VzIG5vdGVib29rLiAKSXQgYSBwcmUtcHJvY2Vzc2VkIFthc3Ryb2N5dG9tYSBtaWNyb2FycmF5IGRhdGFzZXRdKGh0dHBzOi8vd3d3LnJlZmluZS5iaW8vZXhwZXJpbWVudHMvR1NFNDQ5NzEvZ2VuZS1leHByZXNzaW9uLWRhdGEtZnJvbS1waWxvY3l0aWMtYXN0cm9jeXRvbWEtdHVtb3VyLXNhbXBsZXMtYW5kLW5vcm1hbC1jZXJlYmVsbHVtLWNvbnRyb2xzKSB0aGF0IHdlIHBlcmZvcm1lZCBbZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzZXNdKC4vc2NyaXB0cy8wMC1zZXR1cC1pbnRyby10by1SLlIpLgoKKipNb3JlIHRpZHl2ZXJzZSByZXNvdXJjZXM6KiogIAotIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKQotIFtUaWR5dmVyc2UgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLykKLSBbQ2hlYXRzaGVldCBvZiBUaWR5dmVyc2UgZGF0YSB0cmFuc2Zvcm1hdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9kYXRhLXRyYW5zZm9ybWF0aW9uLnBkZikKLSBbT25saW5lIFRpZHl2ZXJzZSBib29rIGNoYXB0ZXJdKGh0dHBzOi8vcHJpdmVmbC5naXRodWIuaW8vYWR2cjM4Ym9vay90aWR5dmVyc2UuaHRtbCkKCioqQ29uY2VwdHMgdG8gY292ZXIgKGluIG9yZGVyKSoqIAotIFJlZmVyZW5jaW5nIGEgbGlicmFyeSdzIGZ1bmN0aW9uIHdpdGggYDo6YAotIGRwbHlyOjpzZWxlY3QKLSBkcGx5cjo6ZmlsdGVyCi0gZHBseXI6Om11dGF0ZQotIGFwcGx5IGZ1bmN0aW9ucz8KLSBXcml0aW5nIHRvIFJEUy9UU1YgCgojIyBTZXQgVXAKCkRlY2xhcmUgdGhlIG5hbWUgb2YgdGhlIGRpcmVjdG9yeSB3aGVyZSB3ZSB3aWxsIHJlYWQgaW4gdGhlIGRhdGEuIAoKYGBge3J9CmRhdGFfZGlyIDwtICJkYXRhIgpgYGAKClRoZSBgdGlkeXZlcnNlYCBpcyBhIGNvbGxlY3Rpb24gb2YgcGFja2FnZXMgdGhhdCBhcmUgaGFuZHkgZm9yIGdlbmVyYWwgZGF0YSAKd3JhbmdsaW5nLCBhbmFseXNpcywgYW5kIHZpc3VhbGl6YXRpb24uIApPdGhlciBwYWNrYWdlcyB0aGF0IGFyZSBzcGVjaWZpY2FsbHkgaGFuZHkgZm9yIGRpZmZlcmVudCBiaW9sb2dpY2FsIGFuYWx5c2VzIGFyZSAKZm91bmQgb24gW0Jpb2NvbmR1Y3Rvcl0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy8pLgpJZiB3ZSB3YW50IHRvIHVzZSBhIHBhY2thZ2UncyBmdW5jdGlvbnMgd2UgZmlyc3QgbmVlZCB0byBpbnN0YWxsIHRoZW0uCkluIG91ciBEb2NrZXIgY29udGFpbmVyLCB3ZSBhbHJlYWR5IGhhdmUgYHRpZHl2ZXJzZWAgYW5kIG90aGVyIHBhY2thZ2VzIHdlIHdpbGwKdXNlIGluIHRoaXMgd29ya3Nob3AgaW5zdGFsbGVkIGZvciB5b3UuIApCdXQgaWYgeW91IG5lZWRlZCB0byBpbnN0YWxsIGl0IG9yIG90aGVyIHBhY2thZ2VzIGF2YWlsYWJsZSBvbiBDUkFOLCB5b3UgCmRvIGl0IHVzaW5nIHRoZSBgaW5zdGFsbC5wYWNrYWdlc2AgZnVuY3Rpb24gbGlrZSB0aGlzOiBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIilgCgpgYGB7ciBMb2FkIHRpZHl2ZXJzZX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMjIFJlZmVyZW5jaW5nIGEgbGlicmFyeSdzIGZ1bmN0aW9uIHdpdGggYDo6YAoKTm90ZSB0aGF0IGlmIHdlIGhhZCBub3QgaW1wb3J0ZWQgdGhlIHRpZHl2ZXJzZSBzZXQgb2YgcGFja2FnZXMgdXNpbmcgYGxpYnJhcnlgIApsaWtlIGFib3ZlLCBhbmQgd2Ugd2VyZSB1c2luZyBhIHRpZHl2ZXJzZSBmdW5jdGlvbiBsaWtlIGByZWFkX3RzdmAsIHdlIHdvdWxkIG5lZWQgdG8gCnRlbGwgUiB3aGF0IHBhY2thZ2UgdG8gZmluZCB0aGlzIGZ1bmN0aW9uIGluLgpUbyBkbyB0aGlzLCB3ZSB3b3VsZCB1c2UgYDo6YCB0byB0ZWxsIFIgdG8gbG9hZCBpbiB0aGlzIGZ1bmN0aW9uIGZyb20gdGhlIApgcmVhZHJgIHBhY2thZ2UgYnkgdXNpbmcgYHJlYWRyOjpyZWFkX3RzdmAuCllvdSB3aWxsIHNlZSB0aGlzIGA6OmAgbWV0aG9kIG9mIHJlZmVyZW5jaW5nIGxpYnJhcmllcyB0aHJvdWdob3V0IHRoZSBjb3Vyc2UuICAKCiMjIE1hbmFnaW5nIGRpcmVjdG9yaWVzCgpCZWZvcmUgd2UgY2FuIGltcG9ydCB0aGUgZGF0YSB3ZSBuZWVkLCB3ZSBzaG91bGQgZG91YmxlIGNoZWNrIHdoZXJlIFIgaXMgCmxvb2tpbmcgZm9yIGZpbGVzIGFrYSBvdXIgd2hhdCBvdXIgKndvcmtpbmcqIGRpcmVjdG9yeSBjdXJyZW50bHkgaXMuIApXZSBjYW4gZG8gdGhpcyBieSB1c2luZyB0aGUgYGdldHdkKClgIGZ1bmN0aW9uLCB3aGljaCB3aWxsIHRlbGwgdXMgd2hhdCBmb2xkZXIKd2UgYXJlIGluLiAKCmBgYHtyfQojIExldCdzIGNoZWNrIHdoYXQgZGlyZWN0b3J5IHdlIGFyZSBpbjoKZ2V0d2QoKQpgYGAKCk5vdGUgdGhhdCBmb3IgUm1kIGZpbGVzLCB0aGUgd29ya2luZyBkaXJlY3RvcnkgaXMgYWx3YXlzIHdoZXJldmVyIHRoZSBmaWxlIGlzIApsb2NhdGVkLiAKCldlIHdpbGwgd2FudCB0byBtYWtlIGEgZGlyZWN0b3J5IGZvciBvdXIgb3V0cHV0IGFuZCB3ZSB3aWxsIGNhbGwgdGhpcyBkaXJlY3Rvcnk6IApgcmVzdWx0c2AuIApCdXQgYmVmb3JlIHdlIGNyZWF0ZSB0aGUgZGlyZWN0b3J5LCB3ZSBzaG91bGQgY2hlY2sgaWYgaXQgYWxyZWFkeSBleGlzdHMuIApXZSB3aWxsIHNob3cgdHdvIHdheXMgdGhhdCB3ZSBjYW4gZG8gdGhpcy4gCgpGaXJzdCwgd2UgY2FuIHVzZSB0aGUgYGRpcigpYCBmdW5jdGlvbiB0byBoYXZlIFIgbGlzdCB0aGUgZmlsZXMgaW4gb3VyIHdvcmtpbmcgCmRpcmVjdG9yeS4gCgpgYGB7cn0KIyBMZXQncyBjaGVjayB3aGF0IGZpbGVzIGFyZSBoZXJlCmRpcigpCmBgYAoKVGhpcyBzaG93cyB1cyB0aGVyZSBpcyBubyBmb2xkZXIgY2FsbGVkICJyZXN1bHRzIiB5ZXQuIAoKSWYgd2Ugd2FudCB0byBtb3JlIHBvaW50ZWRseSBsb29rIGZvciAicmVzdWx0cyIgaW4gb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHdlIGNhbiAKdXNlIHRoZSBgZGlyLmV4aXN0c2AgZnVuY3Rpb24uCgpgYGB7cn0KIyBDaGVjayBpZiB0aGUgcmVzdWx0cyBkaXJlY3RvcnkgZXhpc3RzCmRpci5leGlzdHMoInJlc3VsdHMiKQpgYGAKCklmIHRoZSBhYm92ZSBzYXlzIGBGQUxTRWAgdGhhdCBtZWFucyB3ZSB3aWxsIG5lZWQgdG8gY3JlYXRlIGEgYHJlc3VsdHNgIGRpcmVjdG9yeQp1c2luZyB0aGUgZnVuY3Rpb24gYGRpci5jcmVhdGVgLgoKYGBge3J9CiMgTWFrZSBhIGRpcmVjdG9yeSB3aXRoaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5IGNhbGxlZCAncmVzdWx0cycKZGlyLmNyZWF0ZSgicmVzdWx0cyIpCmBgYAoKQWZ0ZXIgY3JlYXRpbmcgdGhlIHJlc3VsdHMgZGlyZWN0b3J5IGFib3ZlLCBsZXQncyByZS1ydW4gYGRpci5leGlzdHNgIHRvIHNlZSBpZiAKbm93IGl0IGV4aXN0cy4KCmBgYHtyfQojIFJlLWNoZWNrIGlmIHRoZSByZXN1bHRzIGRpcmVjdG9yeSBleGlzdHMKZGlyLmV4aXN0cygicmVzdWx0cyIpCmBgYAoKV2UgY2FuIHVzZSB0aGUgb3V0cHV0IG9mIGBkaXIuZXhpc3RzYCB0byBhdXRvbWF0aWNhbGx5IGNyZWF0ZSBvciBob2xkIG9mZiBvbiAKY3JlYXRpbmcgYSBkaXJlY3RvcnkgYnkgcHV0dGluZyB0aGlzIHRvZ2V0aGVyIGluIGFuIGBpZmAgc3RhdGVtZW50IGxpa2UgYmVsb3cuIApOb3RlIHRoYXQgd2UgcHV0IGFuIGV4Y2xhbWF0aW9uIHBvaW50IHRvIHNpZ25pZnkgdGhhdCB3ZSB3YW50IGEgZGlyZWN0b3J5IHRvIGJlCmNyZWF0ZWQgb25seSAqaWYqIGBkaXIuZXhpc3RzKHJlc3VsdHMpYCBpcyBOT1QgZXF1YWwgdG8gYFRSVUVgLgoKYGBge3J9CiMgSWYgJ3Jlc3VsdHMnIGRpcmVjdG9yeSBkb2Vzbid0IGV4aXN0Li4uCmlmICghZGlyLmV4aXN0cygicmVzdWx0cyIpKSB7CiAgIyAuLi4gY3JlYXRlIGEgJ3Jlc3VsdHMnIGRpcmVjdG9yeQogIGRpci5jcmVhdGUoInJlc3VsdHMiKQp9CmBgYAoKTm90ZSB0aGF0IHRoZSBgZGlyLmV4aXN0c2AgZnVuY3Rpb24gd2lsbCBub3Qgd29yayBvbiBmaWxlcyB0aGVtc2VsdmVzLgpJbiB0aGlzIGNhc2UsIHRoZXJlIGlzIGFuIGFuYWxvZ291cyBmdW5jdGlvbiBjYWxsZWQgYGZpbGUuZXhpc3RzYC4KClRyeSB1c2luZyB0aGUgYGZpbGUuZXhpc3RzYCBmdW5jdGlvbiB0byBzZWUgaWYgdGhlIGZpbGUgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGZpbGUgCmV4aXN0cyBpbiB0aGUgY3VycmVudCBkaXJlY3RvcnkuClVzZSB0aGUgY29kZSBjaHVuayB3ZSBzZXQgdXAgZm9yIHlvdSBiZWxvdy4gCk5vdGUgdGhhdCBpbiBvdXIgbm90ZWJvb2tzIChhbmQgc29tZXRpbWVzIGVsc2V3aGVyZSksIHdoZXJldmVyIHlvdSBzZWUgYSAKYDxGSUxMX0lOX1RIRV9CTEFOSz5gIGxpa2UgaW4gdGhlIGNodW5rIGJlbG93LCB0aGF0IGlzIG1lYW50IGZvciB5b3UgdG8gcmVwbGFjZSAKd2l0aCB0aGUgY29ycmVjdCBwaHJhc2UgYmVmb3JlIHlvdSBydW4gdGhlIGNodW5rIChvdGhlcndpc2UgeW91IHdpbGwgZ2V0IGFuIGVycm9yKS4KCmBgYHtyIGV2YWw9RkFMU0V9CiMgUmVwbGFjZSB0aGUgPFBVVF9GSUxFX05BTUVfSEVSRT4gd2l0aCB0aGUgbmFtZSBvZiB0aGUgZmlsZSB5b3UgYXJlIGxvb2tpbmcgZm9yCiMgUmVtZW1iZXIgdG8gdXNlIHF1b3RlcyB0byBtYWtlIGl0IGEgY2hhcmFjdGVyIHN0cmluZwpmaWxlLmV4aXN0cyg8UFVUX0ZJTEVfTkFNRV9IRVJFPikKYGBgCgpOb3cgdGhhdCB3ZSd2ZSBkZXRlcm1pbmVkIHRoYXQgYGdlbmVfcmVzdWx0c19HU0U0NDk3MS50c3ZgIGV4aXN0cywgd2UgYXJlIHJlYWR5IHRvIHJlYWQgaXQgCmludG8gb3VyIFIgZW52aXJvbm1lbnQuCgojIyMjIFJlYWQgYSBUU1YgZmlsZQoKSW4gUiwgd2Ugd2lsbCB1c2UgYGZpbGUucGF0aCgpYCBmb3IgdGhpcyAobW9yZSBvbiB0aGlzIGxhdGVyIGluIHRoaXMgbm90ZWJvb2spLiAgCkFsdGhvdWdoIGJhc2UgUiBoYXMgcmVhZC1pbiBmdW5jdGlvbnMsIHRoZSBgcmVhZHJgIGZ1bmN0aW9ucyBhcmUgZmFzdGVyIGFuZCBtb3JlCnN0cmFpZ2h0Zm9yd2FyZCB0byB1c2Ugc28gd2UgYXJlIGdvaW5nIHRvIHVzZSB0aG9zZSBoZXJlLiAKQmVjYXVzZSB0aGUgZmlsZSB3ZSBhcmUgcmVhZGluZyBpbiBpcyBhIFRTViAodGFiIHNlcGFyYXRlZCB2YWx1ZXMpIGZpbGUgd2Ugd2lsbCAKYmUgdXNpbmcgdGhlIGByZWFkX3RzdmAgZnVuY3Rpb24uIApCdXQgbm90ZSB0aGF0IHRoZXJlIGFyZSBhbmFsb2dvdXMgZnVuY3Rpb25zIGZvciBDU1YgKGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMpIApmaWxlcyAoYHJlYWRfY3N2YCkgYW5kIG90aGVyIGZpbGVzIHR5cGVzLgoKIyMgUmVhZCBpbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0cyBmaWxlCgpIZXJlIHdlIGFyZSB1c2luZyBhIGB0aWR5dmVyc2VgIGZ1bmN0aW9uIGByZWFkX3RzdmAgZnJvbSBhIGxpYnJhcnkgb2YgZnVuY3Rpb25zIGNhbGxlZCBgcmVhZHJgLiAKCmBgYHtyfQpzdGF0c19kZiA8LSByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKGRhdGFfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3Jlc3VsdHNfR1NFNDQ5NzEudHN2IikpCmBgYApgYGB7cn0KIyBUaGUgZmlsZSB3ZSBhcmUgcmVhZGluZyBpbiBpcyBvdXIgYXJndW1lbnQgZm9yIHJlYWRfdHN2CmdlbmVfZGYgPC0gcmVhZF90c3YoZmlsZS5wYXRoKGRhdGFfZGlyLCAiR1NFNDQ5NzEudHN2IikpCmBgYAoKVXNlIHRoaXMgY2h1bmsgdG8gZXhwbG9yZSB3aGF0IGBnZW5lX2RmYCBsb29rcyBsaWtlLiAKCmBgYHtyfQojIEV4cGxvcmUgYGdlbmVfZGZgCmBgYAoKV2hhdCBpbmZvcm1hdGlvbiBpcyBjb250YWluZWQgaW4gYGdlbmVfZGZgPwoKIyMgZHBseXIgcGlwZXMKCkEgbmlmdHkgdGhpbmcgd2l0aCB0aGUgdGlkeXZlcnNlIGlzIHRoZSBwaXBlczogYCU+JWAKVGhpcyBoYW5keSB0aGluZyBhbGxvd3MgeW91IHRvIGZ1bm5lbCB0aGUgcmVzdWx0IG9mIG9uZSBleHByZXNzaW9uIHRvIHRoZSBuZXh0Cm1ha2luZyB5b3VyIGNvZGUgYSBsaXR0bGUgbW9yZSBzdHJlYW1saW5lZC4KCkZvciBleGFtcGxlLCB0aGUgb3V0cHV0IGZyb20gdGhpczogIAoKYGBge3J9CmZpbHRlcihzdGF0c19kZiwgY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIikKYGBgICAKCi4uLmlzIHRoZSBzYW1lIGFzIHRoZSBvdXRwdXQgZnJvbSB0aGlzOiAgCgpgYGB7cn0Kc3RhdHNfZGYgJT4lIGZpbHRlcihjb250cmFzdCA9PSAibWFsZV9mZW1hbGUiKQpgYGAgIAogIApUaGlzIG1ha2VzIGl0IHNvIHlvdXIgY29kZSBpcyBjbGVhbmVyIGFuZCBlYXNpZXIgdG8gcmVhZC4gCkxldCdzIGxvb2sgYXQgYW4gZXhhbXBsZSB3aXRoIG91ciBzdGF0cyBvZiBvZiBob3cgdGhlIHNhbWUgCmZ1bmN0aW9ucyBsb29rIHdpdGggb3Igd2l0aG91dCBwaXBlczoKCipFeGFtcGxlIDE6KiB3aXRob3V0IHBpcGVzOiAKCmBgYHtyfQpzdGF0c19ub3BpcGUgPC0gc3RhdHNfZGYKc3RhdHNfbm9waXBlIDwtIGFycmFuZ2Uoc3RhdHNfbm9waXBlLCB0X3N0YXRpc3RpYykKc3RhdHNfbm9waXBlIDwtIGZpbHRlcihzdGF0c19ub3BpcGUsIGF2Z19leHByZXNzaW9uID4gNSkKc3RhdHNfbm9waXBlIDwtIHNlbGVjdChzdGF0c19ub3BpcGUsIGNvbnRyYXN0LCBsb2dfZm9sZF9jaGFuZ2UsIHBfdmFsdWUpCmBgYAogIApVR0gsIHdlIGhhdmUgdG8gcmVwZWF0IGFuZCBhc3NpZ24gYHN0YXRzX25vcGlwZWAgc28gbWFueSB0aW1lcyBoZXJlISAKSXQncyBhbm5veWluZyBhbmQgbWFrZXMgaXQgaGFyZGVyIGZvciBwZW9wbGUgdG8gcmVhZC4gCiAgCipFeGFtcGxlIDI6KiBTYW1lIHJlc3VsdCBhcyAxIGJ1dCB3aXRoIHBpcGVzIQoKYGBge3J9CiMgRXhhbXBsZSBvZiB0aGUgc2FtZSBtb2RpZmljYXRpb25zIGFzIGFib3ZlIGJ1dCB3aXRoIHBpcGVzIQpzdGF0c19waXBlICA8LSBzdGF0c19kZiAlPiUKICAgICAgICAgICAgICAgYXJyYW5nZSh0X3N0YXRpc3RpYykgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihhdmdfZXhwcmVzc2lvbiA+IDUpICU+JQogICAgICAgICAgICAgICBzZWxlY3QoY29udHJhc3QsIGxvZ19mb2xkX2NoYW5nZSwgcF92YWx1ZSkKYGBgCgpMZXQncyBkb3VibGUgY2hlY2sgdGhhdCB0aGVzZSBhcmUgdGhlIHNhbWUgYnkgdXNpbmcgdGhlIGZ1bmN0aW9uLCBhbGwuZXF1YWw6IAoKYGBge3J9CmFsbC5lcXVhbChzdGF0c19ub3BpcGUsIHN0YXRzX3BpcGUpCmBgYAoKYGFsbC5lcXVhbGAgaXMgbGV0dGluZyB1cyBrbm93IHRoYXQgdGhlc2UgdHdvIG9iamVjdHMgYXJlIHRoZSBzYW1lLiAKKFlvdSBjYW4gdXNlIGFsbC5lcXVhbCBpbiBvdGhlciBpbnN0YW5jZXMgYW5kIGFkanVzdCB0aGUgcGFyYW1ldGVycyBkZXBlbmRpbmcgb24KaG93IGV4YWN0IG9mIGEgbWF0Y2ggeW91IGFyZSBsb29raW5nIGZvci4gKQoKTm93IHRoYXQgaG9wZWZ1bGx5IHlvdSBhcmUgY29udmluY2VkIHRoYXQgdGlkeXZlcnNlIGhlbHBzIHlvdXIgY29kZSB0byBiZSBuZWF0ZXIgCmFuZCBlYXNpZXIgdG8gdXNlIGFuZCByZWFkLCBsZXQncyBnbyB0aHJvdWdoIHNvbWUgb2YgdGhlIHBvcHVsYXIgdGlkeXZlcnNlIApmdW5jdGlvbnMgYW5kIHNvIHdlIGNhbiBjcmVhdGUgcGlwZWxpbmVzIGxpa2UgdGhpcy4gCgojIyBDb21tb24gdGlkeXZlcnNlIGZ1bmN0aW9ucwoKTGV0J3Mgc2F5IHdlIHdhbnRlZCB0byBmaWx0ZXIgdGhpcyBnZW5lIGV4cHJlc3Npb24gZGF0YXNldCB0byBwYXJ0aWN1bGFyIHNhbXBsZQpncm91cHMuCkluIG9yZGVyIHRvIGRvIHRoaXMsIHdlIHdvdWxkIHVzZSB0aGUgZnVuY3Rpb24gYGZpbHRlcmAgYXMgd2VsbCBhcyBhIGxvZ2ljIHN0YXRlbWVudC4KCmBgYHtyfQojIEhlcmUgbGV0J3MgZmlsdGVyIHRoZSBkYXRhIHRvIGEgcGFydGljdWxhciBnZW5lCnN0YXRzX2RmICU+JSBmaWx0ZXIoZ2VuZV9zeW1ib2wgPT0gIlNOQ0EiKQpgYGAKCldlIGNhbiB1c2UgZmlsdGVyIHNpbWlsYXJseSBmb3IgbnVtZXJpYyBzdGF0ZW1lbnRzLiAgCgpgYGB7cn0KIyBIZXJlIGxldCdzIGZpbHRlciB0aGUgZGF0YSB0byBoYXZlIGF2ZXJhZ2UgZXhwcmVzc2lvbiB2YWx1ZXMgYWJvdmUgNQpzdGF0c19kZiAlPiUgZmlsdGVyKGF2Z19leHByZXNzaW9uID4gNSkKYGBgCgpXZSBjYW4gYXBwbHkgbXVsdGlwbGUgZmlsdGVycyBhdCBvbmNlOgoKYGBge3IgfQpzdGF0c19kZiAlPiUgZmlsdGVyKGNvbnRyYXN0ID09ICJtYWxlX2ZlbWFsZSIsIAogICAgICAgICAgICAgICAgICAgIGF2Z19leHByZXNzaW9uID4gNSkKYGBgCgpXaGVuIHdlIGFyZSBmaWx0ZXJpbmcsIHRoZSBgJWluJWAgb3BlcmF0b3IgY2FuIGNvbWUgaW4gaGFuZHkgaWYgd2UgaGF2ZSBtdWx0aXBsZQppdGVtcyB3ZSB3b3VsZCBsaWtlIHRvIG1hdGNoLgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IHVzaW5nIGAlaW4lYCBkb2VzLgoKYGBge3J9CnN0YXRzX2RmJGdlbmVfc3ltYm9sICVpbiUgYygiU05DQSIsICJDREtOMUEiKQpgYGAKCmAlaW4lYCByZXR1cm5zIGEgbG9naWNhbCB2ZWN0b3IgdGhhdCBub3cgd2UgY2FuIHVzZSBpbiBgZHBseXI6OmZpbHRlcmAuCgpgYGB7ciB9CnN0YXRzX2RmICU+JSBmaWx0ZXIoZ2VuZV9zeW1ib2wgJWluJSBjKCJTTkNBIiwgIkNES04xQSIpKQpgYGAKCkxldCdzIHJldHVybiB0byBvdXIgZmlyc3QgYGZpbHRlcmAgYW5kIGJ1aWxkIG9uIHRvIGl0LiAKVGhpcyB0aW1lLCBsZXQncyBrZWVwIG9ubHkgc29tZSBvZiB0aGVzZSB2YXJpYWJsZXMgd2l0aCB0aGUgYHNlbGVjdGAgZnVuY3Rpb24uIApMZXQncyBhbHNvIHNhdmUgdGhpcyBhcyBhIG5ldyBkYXRhLmZyYW1lIGNhbGxlZCBgc3RhdHNfZmlsdGVyZWRfZGZgLgoKYGBge3J9CnN0YXRzX2ZpbHRlcmVkX2RmIDwtIHN0YXRzX2RmICU+JSAKICBmaWx0ZXIoY29udHJhc3QgPT0gIm1hbGVfZmVtYWxlIiwgCiAgICAgICAgIGF2Z19leHByZXNzaW9uID4gNSkgJT4lCiAgc2VsZWN0KGxvZ19mb2xkX2NoYW5nZSwgdF9zdGF0aXN0aWMpCmBgYAoKTGV0J3Mgc2F5IHdlIHdhbnRlZCB0byBhcnJhbmdlIHRoaXMgZGF0YXNldCBzbyB0aGF0IHRoZSBwYXRpZW50cyB3ZXJlIGFycmFuZ2VkCnlvdW5nZXN0IHRvIG9sZGVzdC4KSW4gb3JkZXIgdG8gZG8gdGhpcywgd2Ugd291bGQgdXNlIHRoZSBmdW5jdGlvbiBgYXJyYW5nZWAgYXMgd2VsbCBhcyB0aGUgdmFyaWFibGUKd2Ugd291bGQgbGlrZSB0byBzb3J0IGJ5IChpbiB0aGlzIGNhc2UgYGFnZV9tdGhzYCkuCgpgYGB7cn0Kc3RhdHNfZGYgJT4lIGFycmFuZ2UocF92YWx1ZSkgCmBgYAoKV2hhdCBpZiB3ZSB3YW50IHRoZSB0aGUgb2xkZXN0IHBhdGllbnRzIGF0IHRoZSB0b3Agb2YgdGhpcyBkYXRhc2V0IGluc3RlYWQ/IApXZSBjYW4gdXNlIHRoZSBzYW1lIGZ1bmN0aW9uLCBidXQgaW5zdGVhZCB1c2UgdGhlIGBkZXNjYCBmdW5jdGlvbiB0b28uIAoKYGBge3J9CnN0YXRzX2RmICU+JSBhcnJhbmdlKGRlc2MocF92YWx1ZSkpCmBgYCAKCldoYXQgaWYgd2Ugd291bGQgbGlrZSB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcz8gCkZvciB0aGF0IHdlIHVzZSBgbXV0YXRlYCBmdW5jdGlvbi4gCgpgYGB7cn0Kc3RhdHNfZGYgJT4lIAogIG11dGF0ZShsb2cxMCA9IC1sb2cxMChwX3ZhbHVlKSkKYGBgCgpXaGF0IGlmIHdlIHdhbnQgdG8gb2J0YWluIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSB2YXJpYWJsZT8gClRoZSBgc3VtbWFyaXplYCBmdW5jdGlvbiBhbGxvd3MgdXMgdG8gY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBjb2x1bW4uIApIZXJlIHdlIHdpbGwgdXNlIHN1bW1hcml6ZSB0byBvYnRhaW4gYW4gYXZlcmFnZSBleHByZXNzaW9uIG92ZXJhbGwgYWxsIGdlbmVzLiAKCmBgYHtyfQpzdGF0c19kZiAlPiUgc3VtbWFyaXplKG1lYW4obG9nX2ZvbGRfY2hhbmdlKSkKYGBgCgpXaGF0IGlmIHdlJ2QgbGlrZSB0byBvYnRhaW4gYSBzdW1tYXJ5IHN0YXRpc3RpY3MgYnV0IGhhdmUgdGhlbSBmb3IgdmFyaW91cyBncm91cHM/CkNvbnZlbmllbnRseSBuYW1lZCwgdGhlcmUncyBhIGZ1bmN0aW9uIGNhbGxlZCBgZ3JvdXBfYnlgIHRoYXQgc2VhbWxlc3NseSAKYWxsb3dzIHVzIHRvIGRvIHRoaXMuIApBbHNvIG5vdGUgdGhhdCBgZ3JvdXBfYnlgIGFsbG93cyB1cyB0byBncm91cCBieSBtdWx0aXBsZSB2YXJpYWJsZXMgYXQgYSB0aW1lIGlmIAp5b3Ugd2FudCB0by4KCmBgYHtyfQpzdGF0c19zdW1tYXJ5X2RmIDwtIHN0YXRzX2RmICU+JQogICAgICBncm91cF9ieShjb250cmFzdCkgJT4lIAogICAgICBzdW1tYXJpemUobWVhbihsb2dfZm9sZF9jaGFuZ2UpKQpgYGAKCkxldCdzIGxvb2sgYXQgYSBwcmV2aWV3IG9mIHdoYXQgd2UgbWFkZToKCmBgYHtyfQpzdGF0c19zdW1tYXJ5X2RmCmBgYAoKIyMjIENodW5raW5nIG91dCB5b3VyIGNvZGU6IGEgdGVjaG5pcXVlIGZvciBmaWd1cmluZyBvdXQgd2hhdCdzIGhhcHBlbmluZwoKV2UgZGlkIHF1aXRlIGEgZmV3IHN0ZXBzIHRvIGdldCB0aGUgb2JqZWN0IGBzdGF0c19zdW1tYXJ5X2RmYC4gCklmIHdlIG5lZWRlZCB0byB0cm91Ymxlc2hvb3QgdGhpcyBzdGVwLCBvciBpZiB3ZSBnb3QgY29uZnVzZWQgb24gd2hhdCdzIApoYXBwZW5pbmcsIGl0IGNvdWxkIGJlIGRpZmZpY3VsdCB0byBmaWd1cmUgdGhpcyBvdXQgYnkgbG9va2luZyBhdCB0aGUgY29kZSBjaHVuayAKYWxsIGF0IG9uY2UuIAoKRm9yIHRoZXNlIHNpdHVhdGlvbnMsIHdlIGFyZSBnb2luZyB0byBpbnRyb2R1Y2UgeW91IHRvIGEgdGVjaG5pcXVlIGNhbGxlZCAKImNodW5raW5nIi4KClByaW50aW5nIG91dCBzbWFsbGVyIHVuaXRzIG9mIGNvZGUgYXQgYSB0aW1lIGNhbiBoZWxwIHlvdSBsZWFybiB3aGF0IGlzIGhhcHBlbmluZwphdCBlYWNoIHN0ZXAuIApUaGlzIHdpbGwgaGVscCBmYXN0LXRyYWNrIHlvdXIgdHJvdWJsZXNob290aW5nIGF0dGVtcHRzLiAKClVzZSB0aGlzIGVtcHR5IGNvZGUgY2h1bmsgYmVsb3cgdG8gdHJ5IGNodW5raW5nIG91dCB0aGlzIGNvZGUgY2h1bmsgYnkgZm9sbG93aW5nCnRoZSBmaWd1cmUgYWJvdmUuIAoKYGBge3J9CiMgVHJ5IHRvIGNodW5rIG91dCBsaW5lcyBvZiBjb2RlIHdlIHVzZWQgdG8gZ2V0IGBzdGF0c19zdW1tYXJ5X2RmYApgYGAKCkZvciBtb3JlIHRyb3VibGVzaG9vdGluZyB0ZWNobmlxdWVzIG9yIGEgYnJlYWtkb3duIG9mIHRoZSBtb3N0IGNvbW1vbiBSIGVycm9ycywgCmNoZWNrIG91dCB0aGUgW2RlYnVnZ2luZyByZXNvdXJjZXMgZ3VpZGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLW1vZHVsZXMvYmxvYi9tYXN0ZXIvaW50cm8tdG8tUi10aWR5dmVyc2UvMDBiLWRlYnVnZ2luZ19yZXNvdXJjZXMubWQpCmluIHRoaXMgcmVwb3NpdG9yeS4KCiMjIEEgYnJpZWYgaW50cm8gdG8gdGhlIGBhcHBseWAgZmFtaWx5IG9mIGZ1bmN0aW9ucwoKSW4gYmFzZSBSIHRoZXJlIHRoZSBgYXBwbHlgIGZhbWlseSBvZiBmdW5jdGlvbnMgY2FuIGJlIGFuIGFsdGVybmF0aXZlIAptZXRob2RzIGZvciBwZXJmb3JtaW5nIHRyYW5zZm9ybWF0aW9ucyBhY3Jvc3MgYSBkYXRhLmZyYW1lLCBtYXRyaXggb3IgZm9yIG90aGVyIApvYmplY3Qgc3RydWN0dXJlcy4gCgpUaGUgc2Vjb25kIGFyZ3VtZW50IGluIGBhcHBseWAgc3BlY2lmaWVzIHdoZXRoZXIgd2UgYXJlIGFwcGx5aW5nIHNvbWV0aGluZyAKYWNyb3NzIHJvd3Mgb3IgYWNyb3NzIGNvbHVtbnMgKDEgZm9yIHJvd3MsIDIgZm9yIGNvbHVtbnMpLgoKYGBge3J9CiMgQ2FsY3VsYXRlIHJvdyBtZWFucwpnZW5lX21lYW5zIDwtIGFwcGx5KGdlbmVfZGZbLCAtMV0sIDEsIG1lYW4pICMgTm90aWNlIHdlIGFyZSB1c2luZyAxIGhlcmUKCiMgSG93IGxvbmcgd2lsbCBgZ2VuZV9tZWFuc2AgYmU/IApsZW5ndGgoZ2VuZV9tZWFucykKYGBgCgpXaHkgZG8gd2UgaGF2ZSBhIGBbLCAtMV1gIGFmdGVyIGBnZW5lX2RmYCBpbiB0aGUgYWJvdmUgY2h1bms/Ck5vdyBsZXQncyBpbnZlc3RpZ2F0ZSB0aGUgc2FtZSBzZXQgdXAsIGJ1dCB1c2UgMiB0byBgYXBwbHlgIG92ZXIgdGhlIGNvbHVtbnMgb2YKb3VyIG1hdHJpeC4KCmBgYHtyfQojIENhbGN1bGF0ZSBzYW1wbGUgbWVhbnMKc2FtcGxlX21lYW5zIDwtIGFwcGx5KGdlbmVfZGZbLCAtMV0sIDIsIG1lYW4pICMgTm90aWNlIHdlIHVzZSAyIGhlcmUKCiMgSG93IGxvbmcgd2lsbCBgc2FtcGxlX21lYW5zYCBiZT8gCmxlbmd0aChzYW1wbGVfbWVhbnMpCmBgYAoKQWx0aG91Z2ggdGhlIGBhcHBseWAgZnVuY3Rpb25zIG1heSBub3QgYmUgYXMgZWFzeSB0byB1c2UgYXMgdGhlIGB0aWR5dmVyc2VgIApmdW5jdGlvbnMsIGZvciBzb21lIGFwcGxpY2F0aW9ucywgYGFwcGx5YCBtZXRob2RzIGNhbiBiZSBiZXR0ZXIgc3VpdGVkLgpJbiB0aGlzIHdvcmtzaG9wLCB3ZSB3aWxsIG5vdCBkZWx2ZSB0b28gZGVlcGx5IGludG8gdGhlIHZhcmlvdXMgb3RoZXIgYXBwbHkgCmZ1bmN0aW9ucyAoYHRhcHBseWAsIGBsYXBwbHlgLCBldGMuKSBidXQgeW91IGNhbiByZWFkIG1vcmUgZXhhbXBsZXMgCmFib3V0IHRoZW0gW2hlcmVdKGh0dHBzOi8vd3d3Lmd1cnU5OS5jb20vci1hcHBseS1zYXBwbHktdGFwcGx5Lmh0bWwpLgojIyBUaGUgZHBseXI6OmpvaW4gZnVuY3Rpb25zCgpMZXQncyBzYXkgd2UgaGF2ZSBhIHNjZW5hcmlvIHdoZXJlIHdlIGhhdmUgdHdvIGRhdGEuZnJhbWVzIHRoYXQgd2Ugd291bGQgbGlrZSB0byAKY29tYmluZS4gClJlY2FsbCB0aGF0IGBzdGF0c19waXBlYCBhbmQgYGdlbmVfZGZgIGFyZSBkYXRhLmZyYW1lcyB0aGF0IGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgc29tZSBvZiB0aGUgc2FtZSBnZW5lcy4KVGhlIFtgZHBseXI6OmpvaW5gIGZhbWlseSBvZiBmdW5jdGlvbnNdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvam9pbi5odG1sKSAKYXJlIHVzZWZ1bCBmb3IgdmFyaW91cyBzY2VuYXJpb3Mgb2YgY29tYmluaW5nIGRhdGEuZnJhbWVzLiAKCkZvciBub3csIHdlIHdpbGwgZm9jdXMgb24gYGlubmVyX2pvaW5gLCB3aGljaCB3aWxsIGNvbWJpbmUgZGF0YS5mcmFtZXMgYnkgb25seQprZWVwaW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzYW1wbGVzIHRoYXQgYXJlIGluIGJvdGggZGF0YS5mcmFtZXMuIApXZSBuZWVkIHRvIHVzZSB0aGUgYGJ5YCBhcmd1bWVudCB0byBkZXNpZ25hdGUgd2hhdCBjb2x1bW4ocykgCnNob3VsZCBiZSB1c2VkIGFzIGEga2V5IHRvIG1hdGNoIHRoZSBkYXRhLmZyYW1lcyAoaW4gdGhpcyBjYXNlIGBlbnNlbWJsX2lkYHMpLiAKCmBgYHtyfQpzdGF0c19kZiAlPiUgaW5uZXJfam9pbihnZW5lX2RmLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCdlbnNlbWJsX2lkJyA9ICdHZW5lJykpIApgYGAKCk5vdGUgdGhhdCBpZiB0aGVyZSBhcmUgY29sdW1ucyBvZiB0aGUgc2FtZSBuYW1lIGluIGJvdGggZGF0YS5mcmFtZXMsIHRoZSBkZWZhdWx0IAppcyB0byBzYXZlIGJvdGggY29sdW1ucyBidXQgYSBgeGAgb3IgYHlgIGlzIGFkZGVkIHRvIHRoZSBuYW1lIHRvIG5vdGUgd2hpY2ggCmRhdGEuZnJhbWUgaXQgb3JpZ2luYXRlZCBmcm9tLgoKIyMgU2F2ZSBkYXRhIHRvIGZpbGVzCgojIyMjIFNhdmUgdG8gVFNWIGZpbGVzCgpMZXQncyB3cml0ZSB0aGlzIGRhdGEgd2UgZWRpdGVkIHRvIGEgZmlsZS4KVG8gZG8gdGhpcywgd2UgY2FuIHVzZSB0aGUgYHJlYWRyYCBsaWJyYXJ5IG9mIGB3cml0ZWAgZnVuY3Rpb25zLiAKTm90ZSB0aGF0IHRoZSBzZWNvbmQgYXJndW1lbnQgb2YgYHdyaXRlX3RzdmAgbmVlZHMgdG8gYmUgYSBjaGFyYWN0ZXIgc3RyaW5nIHRoYXQgCmNvbnRhaW5zIG91ciBgZmlsZS5wYXRoYCB0byB0aGUgbmV3IGZpbGUgd2Ugd291bGQgbGlrZSB0byBjcmVhdGUuClJlbWVtYmVyIHRoYXQgd2UgY3JlYXRlZCBhIGByZXN1bHRzYCBkaXJlY3RvcnkgdG8gcHV0IG91ciBvdXRwdXQgaW4uIApCdXQgaWYgd2Ugd2FudCB0byBzYXZlIG91ciBkYXRhIHRvIGEgZGlyZWN0b3J5IG90aGVyIHRoYW4gb3VyIHdvcmtpbmcgZGlyZWN0b3J5LCAKd2UgbmVlZCB0byBzcGVjaWZ5IHRoaXMuIApUaGlzIGlzIHdoYXQgd2Ugd2lsbCB1c2UgdGhlIGBmaWxlLnBhdGhgIGZ1bmN0aW9uIGZvci4gCkxldCdzIGxvb2sgYXQgd2hhdCBgZmlsZS5wYXRoYCBkb2VzLCBieSB0YWtpbmcgYSBsb29rIGF0IHRoZSBwcmludCBvdXQgb2YgaXQgCmZyb20gZXhhbXBsZXMgYmVsb3cuCgpgYGB7cn0KIyBXaGljaCBvZiB0aGVzZSBmaWxlIHBhdGhzIGlzIHdoYXQgd2Ugd2FudCB0byB1c2UgdG8gc2F2ZSBvdXIgZGF0YSB0byB0aGUKIyByZXN1bHRzIGRpcmVjdG9yeSB3ZSBjcmVhdGVkIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBub3RlYm9vaz8KZmlsZS5wYXRoKCJkb2NrZXItaW5zdGFsbCIsICJzdGF0c19zdW1tYXJ5LnRzdiIpCmZpbGUucGF0aCgicmVzdWx0cyIsICJzdGF0c19zdW1tYXJ5LnRzdiIpCmZpbGUucGF0aCgic3RhdHNfc3VtbWFyeS50c3YiLCAicmVzdWx0cyIpCmBgYAoKUmVwbGFjZSBgPE5FV19GSUxFX1BBVEg+YCBiZWxvdyB3aXRoIHRoZSBgZmlsZS5wYXRoYCBmcm9tIGFib3ZlIHRoYXQgd2lsbCAKc3VjY2Vzc2Z1bGx5IHNhdmUgb3VyIGZpbGUgdG8gdGhlIGByZXN1bHRzYCBmb2xkZXIgCgpgYGB7ciBldmFsPUZBTFNFfQojIFdyaXRlIG91ciBkYXRhLmZyYW1lIHRvIGEgVFNWIGZpbGUKd3JpdGVfdHN2KHN0YXRzX3N1bW1hcnlfZGYsIDxORVdfRklMRV9QQVRIPikKYGBgCgpDaGVjayBpbiB5b3VyIGByZXN1bHRzYCBkaXJlY3RvcnkgdG8gc2VlIGlmIHlvdXIgbmV3IGZpbGUgaGFzIHN1Y2Nlc3NmdWxseSBzYXZlZC4KCiMjIyMgU2F2ZSB0byBSRFMgZmlsZXMKCkZvciB0aGlzIGV4YW1wbGUgd2UgaGF2ZSBiZWVuIHdvcmtpbmcgd2l0aCBgZGF0YS5mcmFtZWBzLCBob3dldmVyLCBpbiBvdGhlciAKc2l0dWF0aW9ucyB3ZSBtYXkgd2FudCB0byBzYXZlIG1vcmUgY29tcGxpY2F0ZWQgc3RydWN0dXJlIG9iamVjdHMgb3IgdmVyeSBsYXJnZSAKb2JqZWN0cy4gClJEUyAoUiBEYXRhIFNlcmlhbGl6ZWQvU2luZ2xlKSBmaWxlcyBtYXkgYmUgYSBiZXR0ZXIgb3B0aW9uIGluIHRoZXNlIGluc3RhbmNlcy4KUkRTIGlzIFIncyBzcGVjaWFsIGZpbGUgZm9ybWF0IGZvciBob2xkaW5nIGRhdGEgZXhhY3RseSBhcyB5b3UgaGF2ZSBpdCBpbiB5b3VyIApSIGVudmlyb25tZW50LiAKUkRTIGZpbGVzIGNhbiBhbHNvIGJlIGNvbXByZXNzZWQsIG1lYW5pbmcgdGhleSB3aWxsIHRha2UgdXAgbGVzcyBzcGFjZSBvbiB5b3VyIApjb21wdXRlci4gCkxldCdzIHNhdmUgb3VyIGRhdGEgdG8gYW4gUkRTIGZpbGUgaW4gb3VyIGByZXN1bHRzYCBmb2xkZXIuCllvdSB3aWxsIG5lZWQgdG8gcmVwbGFjZSB0aGUgYC50c3ZgIHdpdGggYC5SRFNgLCBidXQgeW91IGNhbiB1c2Ugd2hhdCB3ZSAKZGV0ZXJtaW5lZCBhcyBvdXIgYGZpbGUucGF0aGAgZm9yIHRoZSBsYXN0IGNodW5rIGFzIHlvdXIgdGVtcGxhdGUuIAoKYGBge3IgZXZhbD1GQUxTRX0KIyBXcml0ZSB5b3VyIG9iamVjdCB0byBhbiBSRFMgZmlsZQp3cml0ZV9yZHMoc3RhdHNfc3VtbWFyeSwgPFBVVF9DT1JSRUNUX0ZJTEVfUEFUSF9IRVJFPikKYGBgCgojIyMjIFJlYWQgYW4gUkRTIGZpbGUKClNpbmNlIG5vdyB5b3UgaGF2ZSBsZWFybmVkIHRoZSBgcmVhZHJgIGZ1bmN0aW9uczogYHJlYWRfdHN2YCwgYHdyaXRlX3RzdmAsIGFuZCAKbm93LCBgd3JpdGVfcmRzYCwgd2hhdCBkbyB5b3Ugc3VwcG9zZSB0aGUgZnVuY3Rpb24geW91IHdpbGwgbmVlZCB0byByZWFkIHlvdXIgClJEUyBmaWxlIGlzIGNhbGxlZD8gClVzZSB0aGF0IGZ1bmN0aW9uIGhlcmUgdG8gcmUtaW1wb3J0IHlvdXIgZGF0YSBpbiB0aGUgY2h1bmsgd2Ugc2V0IHVwIGZvciB5b3UKYmVsb3cuCgpgYGB7ciBldmFsPUZBTFNFfQojIFJlYWQgaW4geW91ciBSRFMgZmlsZQpyZWltcG9ydF9kZiA8LSA8UFVUX0ZVTkNUSU9OX05BTUU+KGZpbGUucGF0aCgicmVzdWx0cyIsICJzdGF0c19jbGVhbi5SRFMiKSkKYGBgCgpBcyBpcyBnb29kIHByYWN0aWNlLCB3ZSB3aWxsIGVuZCB0aGlzIHNlc3Npb24gYnkgcHJpbnRpbmcgb3V0IG91ciBzZXNzaW9uIGluZm8uIAoKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CiMgUHJpbnQgb3V0IHRoZSB2ZXJzaW9ucyBhbmQgcGFja2FnZXMgd2UgYXJlIHVzaW5nIGluIHRoaXMgc2Vzc2lvbgpzZXNzaW9uSW5mbygpCmBgYAo=